package fun.yao.common.nio;

import fun.yao.common.Constant;
import fun.yao.common.uitls.CommonUtil;
import fun.yao.common.uitls.LogUtil;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Random;

public final class SocketClientThread extends AbstractNetThread
{
    /** 套接字通道 */
    private SocketChannel mSocketChannel = null ;

    /** 二级系统封装的套接字通道 */
    private GessSocketChannel mGessSocketChannel = null;

    /**Socket关闭事件的回调方法*/
    private Method callSocketClosedMethod = null;
    /**Socket关闭事件的回调对象*/
    private Object callSocketClosedObj = null;

    /** 远程主机列表*/
    private ArrayList<InetSocketAddress> mParentAddressList = new ArrayList<InetSocketAddress>();

    /**是否需要压缩/解压缩报文 add by csl 2009.5.21 */
    private boolean bIsNeedZip = false;

    /** 是否已连接 */
    private boolean bIsLinked = false;

    /** 心跳包测试报文 */
    private String mSocketTestMsg = "";

    /** 随机数 */
    private Random random = new Random(System.currentTimeMillis());

    /**是否为短连接*/
    private boolean bIsShort = false;

    /**socket read 接口*/
    private IGessSocketRead gessSocketRead = null;

    /**报文头长度*/
    private int mHeadLen = Constant.MSG_LEN_LEN;

    public SocketClientThread() throws NoSuchMethodException,SecurityException
    {
        this.regReadMethod(this,"doRead");
        this.regWriteMethod(this,"doWrite");
    }

    public void setHeadLen(int headLen)
    {
        mHeadLen = headLen;
    }

    /**
     * 设置socket read
     * @param gessSocketRead
     */
    public void setGessSocketRead(IGessSocketRead gessSocketRead)
    {
        this.gessSocketRead = gessSocketRead;
    }

    /**
     * 设置连接方式
     * @param flag
     */
    public void setIsShort(boolean flag)
    {
        this.bIsShort = flag;
    }

    /**
     * 设置是否需要压缩/解压缩报文
     * @param flag
     */
    public void setIsNeedZip(boolean flag)
    {
        this.bIsNeedZip = flag;
    }

    /**
     * 设置远程主机的相关信息
     * @param sRemoteIp   主机IP地址
     * @param iRemotePort 主机监听端口
     */
    public void setRemoteHost(String sRemoteIp, int iRemotePort)
    {
        this.addRemoteAddress(sRemoteIp,iRemotePort);
    }

    /**
     * 设置心跳包发送报文。在报文中支持使用变量。格式：<变量名>
     * 目前支持的有：
     *   1、流水号：<SeqNo8> 随便生成8位流水号
     * @param sTestMsg
     */
    public void setSocketTestMsg(String sTestMsg)
    {
        this.mSocketTestMsg = sTestMsg;
    }

    /**
     * 添加远程主机地址
     * @param sRemoteIp   IP地址
     * @param iRemotePort 端口
     */
    public void addRemoteAddress(String sRemoteIp, int iRemotePort)
    {
        InetSocketAddress addr = new InetSocketAddress(sRemoteIp,iRemotePort);
        this.mParentAddressList.add(addr);
    }

    /**
     * 添加远程主机地址
     * @param addr InetSocketAddress对象
     */
    public void addRemoteAddress(InetSocketAddress addr)
    {
        this.mParentAddressList.add(addr);
    }

    /**
     * 添加远程主机地址
     */
    public void addRemoteAddress(ArrayList<InetSocketAddress> alAddress )
    {
        for ( int i = 0 ; i < alAddress.size() ; i++ )
        {
            this.mParentAddressList.add(alAddress.get(i));
        }
    }

    /**是否连接状态*/
    public boolean isLinked()
    {
        return this.bIsLinked;
    }

    /**
     * 设置Socket关闭后的回调方法
     * @param objCall      接收Socket关闭事件的回调方法实例
     * @param sMethodName  接收Socket关闭事件的回调方法名称
     * 回调方法原形为：method(SocketChannel vSocketChannel);
     * @throws NoSuchMethodException
     * @throws SecurityException
     */
    public void regSocketClosed(Object objCall, String sMethodName)
            throws NoSuchMethodException,SecurityException
    {
        this.callSocketClosedMethod = objCall.getClass().getMethod(sMethodName,new Class[]{SocketChannel.class});
        this.callSocketClosedObj = objCall;
    }

    /**读取事件处理*/
    public void doRead(Selector selector, SocketChannel sc) throws Exception
    {
        if ( mGessSocketChannel != null )
            this.mGessSocketChannel.doRead();
    }

    /**写入事件处理*/
    public void doWrite(Selector selector, SocketChannel sc) throws Exception
    {
        if ( mGessSocketChannel != null )
            this.mGessSocketChannel.doWrite();
    }

    /**读取到一条完整报文的处理*/
    public  void doReadFullMsg(SocketChannel sc, ByteBuffer sFullMsgBuff) throws Exception
    {
        if ( this.callReadFullMsgMethod != null && this.callReadFullMsgObj != null )
        {
            this.callReadFullMsgMethod.invoke(this.callReadFullMsgObj,new Object[]{sc,sFullMsgBuff});
        }
    }

    /**  关闭SocketChannel */
    public void closeSocketChannel(SocketChannel vSocketChannel)  throws Exception
    {
        try
        {
            bIsLinked = false;

            if ( this.callSocketClosedObj != null && this.callSocketClosedMethod != null )
            {
                this.callSocketClosedMethod.invoke(this.callSocketClosedObj,new Object[]{vSocketChannel});
            }
        }catch(Exception e)
        {
            LogUtil.WriteLog(Constant.NORMAL_ERROR, e);
        }
    }

    /** 连接成功事件处理 */
    protected void doConnectSucceed(Selector selector, SocketChannel sc)
    {
        try
        {
            bIsLinked = true;

            if ( this.callConnectObj != null && this.callConnectMethod != null )
            {
                this.callConnectMethod.invoke(this.callConnectObj, new Object[]{selector,sc});
            }

        }catch(Exception e){
            LogUtil.WriteLog(Constant.NORMAL_ERROR, e);
        }
    }


    /**
     * 添加待写入的消息
     * @param sbWriteMsg 待写入的消息，包括通信部分
     */
    public void addWriteMsg(StringBuffer sbWriteMsg)
    {
        this.mGessSocketChannel.addWriteMsg(sbWriteMsg);
    }

    /**
     * 添加待写入的消息
     * @param bWriteMsgBuff 待写入的消息，包括通信部分
     */
    public void addWriteMsg(ByteBuffer bWriteMsgBuff)
    {
        this.mGessSocketChannel.addWriteMsg(bWriteMsgBuff);
    }

    /**
     * 获得选择器
     * @return
     */
    public Selector getSelector()
    {
        return this.mSelector;
    }

    /**
     * 获得SocketChannel
     * @return
     */
    public SocketChannel getSocketChannel()
    {
        return this.mSocketChannel;
    }


    /**
     * 获得GessSocketChannel
     * @return
     */
    public GessSocketChannel getGessSocketChannel()
    {
        return this.mGessSocketChannel;
    }

    /** 获得连接测试报文 */
    protected String getSendSocketTestMsg()
    {
        String tmp = this.mSocketTestMsg;
        tmp = tmp.replaceAll("<SeqNo8>", CommonUtil.FILL(String.valueOf((long)(random.nextDouble()*100000000)), '0', 8, 'L'));
        return tmp;
    }

    /**
     * 连接服务器
     * @return
     */
    public boolean connect()
    {
        try
        {
            bIsRun = true;

            //创建Selector
            if ( this.mSelector == null || this.mSelector.isOpen() == false )
                this.mSelector = Selector.open();

            //创建SocketChannel
            if ( this.mSocketChannel != null )
            {
                this.mSocketChannel.close();
                this.mSocketChannel = null;
            }

            this.mSocketChannel = this.openSocketChannel();

            //配置非阻塞方式
            this.mSocketChannel.configureBlocking(false);

            //注册读事件
            SelectionKey key = this.mSocketChannel.register(this.mSelector,SelectionKey.OP_READ);

            mGessSocketChannel = new GessSocketChannel(this.mSelector,this.mSocketChannel,key,mHeadLen);
            mGessSocketChannel.regReadFullMsgMethod(this, "doReadFullMsg");
            mGessSocketChannel.regSocketClosedMethod(this, "closeSocketChannel");
            mGessSocketChannel.setEncryptInfo(this.mEncryptMode, this.mSessionId);
            mGessSocketChannel.setIsNeedZip(bIsNeedZip);
            mGessSocketChannel.setGessSocketRead(this.gessSocketRead);

            //发送连接完成事件
            doConnectSucceed(this.mSelector,this.mSocketChannel);

            return true;

        }catch(Exception e)
        {
            try{
                if ( this.mSocketChannel != null )
                    closeSocketChannel(this.mSocketChannel);
            }catch(Exception ee)
            {
                LogUtil.WriteLog(Constant.BADLY_ERROR, ee);
            }
        }

        return false;
    }

    /**
     * 打开连接
     * @return
     * @throws IOException
     */
    private SocketChannel openSocketChannel() throws IOException
    {
        for ( int i = 0 ; i < this.mParentAddressList.size() ; i++ )
        {
            String sIp   = this.mParentAddressList.get(i).getAddress().getHostAddress();
            int    iPort = this.mParentAddressList.get(i).getPort();
            try
            {
                LogUtil.writeSystemLog(Constant.NOTICE,"开始连接[Ip=" + sIp + ",Port=" + iPort + "]...");
                InetSocketAddress address = new InetSocketAddress(sIp,iPort);
                SocketChannel sc = SocketChannel.open(address);
                LogUtil.writeSystemLog(Constant.NOTICE,"连接[Ip=" + sIp + ",Port=" + iPort + "]成功！");
                return sc;
            }catch(IOException ioe)
            {
                LogUtil.writeSystemLog(Constant.NOTICE,"连接[Ip=" + sIp + ",Port=" + iPort + "]失败！");
            }
        }

        throw new IOException("无法连接远程主机");
    }

    /**线程初始化*/
    public void init()
    {
        //长连接,初始化时连接
        if(bIsShort == false)
            connect();
    }

    /** 线程空闲时调用 */
    protected void idlesse()
    {
        //短连接时,直接返回
        if(this.bIsShort == true)
            return;

        try
        {

            if ( bIsLinked == false ||
                    this.mGessSocketChannel == null ||
                    this.mSocketChannel     == null ||
                    this.mSocketChannel.isConnected() == false  )
            {
                //连接Socket
                this.connect();
            }else
            {
                //判断是否响应超时
                if ( this.mGessSocketChannel != null &&  this.mGessSocketChannel.checkIsRecvDataTimeOut(2*4) )
                {
                    LogUtil.writeSystemLog(Constant.NOTICE,"接收上级服务器[" + this.mSocketChannel.socket().toString() + "]消息超时，关闭连接！");
                    this.mGessSocketChannel.sendSocketClosedEvent();
                }else if ( this.mGessSocketChannel != null && this.mGessSocketChannel.checkCanSendTestMsg(2) ) //发送测试报文 是普通定义心跳包报文的2倍时间
                {
                    String tmp = this.getSendSocketTestMsg();
                    if ( tmp != null && tmp.length() > 0 )
                        this.addWriteMsg(new StringBuffer(tmp));
                }
            }
        }catch(Exception e)

        {
            LogUtil.WriteLog(Constant.NORMAL_ERROR,e);
        }
    }

    /**
     * 获得当前线程的运行状态
     * @return
     */
    public boolean getIsRuning()
    {
        return bIsRun;
    }

    /**线程关闭*/
    public void close()
    {
        try
        {
            bIsRun = false;

            if ( this.mSelector != null )
                this.mSelector.close();

            if ( this.mSocketChannel != null )
                this.mSocketChannel.close();

        }catch(Exception e)
        {
            LogUtil.WriteLog(Constant.NORMAL_ERROR,e);
        }
    }

}
